#pragma region License
/*
MIT License
Copyright © 2006 The Mono.Xna Team

All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma endregion License

#ifndef RVector3_h
#define RVector3_h

#include <Defines.h>
#include <CSharpPropertyMethods.h>

#include <System/Collections/Generic/IEquatable.h>
#include <System/Array.h>
#include <System/Math.h>
#include <System/String.h>
#include <System/Math.h>
#include <System/Object.h>

#include "Vector2.h"
#include "RQuaternion.h"

namespace Riccsson
{
	namespace Xna
	{
		namespace Framework
		{
			struct Vector3 : public Riccsson::System::Collections::Generic::IEquatable<Vector3>
			{
				#pragma region private: Fields

				public: static const Vector3& Zero;
				public: static const Vector3& One;
				public: static const Vector3& UnitX;
				public: static const Vector3& UnitY;
				public: static const Vector3& UnitZ;
				public: static const Vector3& Up;
				public: static const Vector3& Down;
				public: static const Vector3& Right;
				public: static const Vector3& Left;
				public: static const Vector3& Forward;
				public: static const Vector3& Backward;

				#pragma endregion private: Fields


				#pragma region public: Fields

				public: float X;
				public: float Y;
				public: float Z;

				#pragma endregion public: Fields


				#pragma region Properties
				/*
				public: static Vector3 Zero
				{
					get { return zero; }
				}

				public: static Vector3 One
				{
					get { return one; }
				}

				public: static Vector3 UnitX
				{
					get { return unitX; }
				}

				public: static Vector3 UnitY
				{
					get { return unitY; }
				}

				public: static Vector3 UnitZ
				{
					get { return unitZ; }
				}

				public: static Vector3 Up
				{
					get { return up; }
				}

				public: static Vector3 Down
				{
					get { return down; }
				}

				public: static Vector3 Right
				{
					get { return right; }
				}

				public: static Vector3 Left
				{
					get { return left; }
				}

				public: static Vector3 Forward
				{
					get { return forward; }
				}

				public: static Vector3 Backward
				{
					get { return backward; }
				}
				*/
				#pragma endregion Properties


				#pragma region Constructors

				public: Vector3();
				public: Vector3(float x, float y, float z);
				public: Vector3(float value);
				public: Vector3(Vector2 value, float z);

				#pragma endregion Constructors


				#pragma region public: Methods

				public: static Vector3 Add(Vector3 value1, Vector3 value2);
				public: static void Add(Vector3 ref value1, Vector3 ref value2, Vector3 out result);
				public: static Vector3 Barycentric(Vector3 value1, Vector3 value2, Vector3 value3, float amount1, float amount2);
				public: static void Barycentric(Vector3 ref value1, Vector3 ref value2, Vector3 ref value3, float amount1, float amount2, Vector3 out result);
				public: static Vector3 CatmullRom(Vector3 value1, Vector3 value2, Vector3 value3, Vector3 value4, float amount);
				public: static void CatmullRom(Vector3 ref value1, Vector3 ref value2, Vector3 ref value3, Vector3 ref value4, float amount, Vector3 out result);
				public: static Vector3 Clamp(Vector3 value1, Vector3 min, Vector3 max);
				public: static void Clamp(Vector3 ref value1, Vector3 ref min, Vector3 ref max, Vector3 out result);
				public: static Vector3 Cross(Vector3 vector1, Vector3 vector2);
				public: static void Cross(Vector3 ref vector1, Vector3 ref vector2, Vector3 out result);
				public: static void DistanceSquared(Vector3 ref value1, Vector3 ref value2, float out result);
				public: static float Distance(Vector3 vector1, Vector3 vector2);
				public: static void Distance(Vector3 ref value1, Vector3 ref value2, float out result);
				public: static float DistanceSquared(Vector3& value1, Vector3& value2);
				public: static Vector3 Divide(Vector3 value1, Vector3 value2);
				public: static Vector3 Divide(Vector3 value1, float value2);
				public: static void Divide(Vector3 ref value1, float divisor, Vector3 out result);
				public: static void Divide(Vector3 ref value1, Vector3 ref value2, Vector3 out result);
				public: static float Dot(Vector3 vector1, Vector3 vector2);
				public: static void Dot(Vector3 ref vector1, Vector3 ref vector2, float out result);
				public: override bool Equals(const Vector3* other) const;
				public: bool Equals(const Riccsson::System::Object* other) const;
				public: override int GetHashCode();
				public: static Vector3 Hermite(Vector3 value1, Vector3 tangent1, Vector3 value2, Vector3 tangent2, float amount);
				public: static void Hermite(Vector3 ref value1, Vector3 ref tangent1, Vector3 ref value2, Vector3 ref tangent2, float amount, Vector3 out result);
				public: float Length();
				public: float LengthSquared();
				public: static Vector3 Lerp(Vector3 value1, Vector3 value2, float amount);
				public: static void Lerp(Vector3 ref value1, Vector3 ref value2, float amount, Vector3 out result);
				public: static Vector3 Max(Vector3 value1, Vector3 value2);
				public: static void Max(Vector3 ref value1, Vector3 ref value2, Vector3 out result);
				public: static Vector3 Min(Vector3 value1, Vector3 value2);
				public: static void Min(Vector3 ref value1, Vector3 ref value2, Vector3 out result);
				public: static Vector3 Multiply(Vector3 value1, Vector3 value2);
				public: static Vector3 Multiply(Vector3 value1, float scaleFactor);
				public: static void Multiply(Vector3 ref value1, float scaleFactor, Vector3 out result);
				public: static void Multiply(Vector3 ref value1, Vector3 ref value2, Vector3 out result);
				public: static Vector3 Negate(Vector3 value);
				public: static void Negate(Vector3 ref value, Vector3 out result);
				public: static Vector3 Normalize(Vector3 vector);
				public: static void Normalize(Vector3 ref value, Vector3 out result);
				public: void Normalize();
				public: static Vector3 Reflect(Vector3 vector, Vector3 normal);
				public: static void Reflect(Vector3 ref vector, Vector3 ref normal, Vector3 out result);
				public: static Vector3 SmoothStep(Vector3 value1, Vector3 value2, float amount);
				public: static void SmoothStep(Vector3 ref value1, Vector3 ref value2, float amount, Vector3 out result);
				public: static Vector3 Subtract(Vector3 value1, Vector3 value2);
				public: static void Subtract(Vector3 ref value1, Vector3 ref value2, Vector3 out result);
				public: override GC_PTR<Riccsson::System::string> ToString();
				public: static Vector3 Transform(Vector3 position, Matrix matrix);
				public: static void Transform(Vector3 ref position, Matrix ref matrix, Vector3 out result);
				public: static void Transform(Riccsson::System::Array<Vector3>* sourceArray, Matrix ref matrix, Riccsson::System::Array<Vector3>* destinationArray);

			/// <summary>
				/// Transforms a vector by a quaternion rotation.
				/// </summary>
				/// <param name="vec">The vector to transform.</param>
				/// <param name="quat">The quaternion to rotate the vector by.</param>
				/// <returns>The result of the operation.</returns>
				public: static Vector3 Transform(Vector3 vec, Quaternion quat);

				/// <summary>
				/// Transforms a vector by a quaternion rotation.
				/// </summary>
				/// <param name="vec">The vector to transform.</param>
				/// <param name="quat">The quaternion to rotate the vector by.</param>
				/// <param name="result">The result of the operation.</param>
		//        public: static void Transform(Vector3 ref vec, ref Quaternion quat, Vector3 out result)
		//        {
		//		// Taken from the OpentTK implementation of Vector3
		//            // Since vec.W == 0, we can optimize quat * vec * quat^-1 as follows:
		//            // vec + 2.0 * cross(quat.xyz, cross(quat.xyz, vec) + quat.w * vec)
		//            Vector3 xyz = quat.Xyz, temp, temp2;
		//            Vector3.Cross(ref xyz, ref vec, out temp);
		//            Vector3.Multiply(ref vec, quat.W, out temp2);
		//            Vector3.Add(ref temp, ref temp2, out temp);
		//            Vector3.Cross(ref xyz, ref temp, out temp);
		//            Vector3.Multiply(ref temp, 2, out temp);
		//            Vector3.Add(ref vec, ref temp, out result);
		//        }

				/// <summary>
				/// Transforms a vector by a quaternion rotation.
				/// </summary>
				/// <param name="vec">The vector to transform.</param>
				/// <param name="quat">The quaternion to rotate the vector by.</param>
				/// <param name="result">The result of the operation.</param>
				public: static void Transform(Vector3 ref value, Quaternion ref rotation, Vector3 out result);
				public: static Vector3 TransformNormal(Vector3 normal, Matrix matrix);
				public: static void TransformNormal(Vector3 ref normal, Matrix ref matrix, Vector3 out result);

				#pragma endregion public: methods

						
				Vector3& operator= (Vector3 value);

				bool operator ==(const Vector3& value2);
				bool operator !=(Vector3 value2);
				Vector3 operator +(Vector3 value2);
				Vector3 operator +=(Vector3 value2);
				Vector3 operator -(const Vector3& value2);
				Vector3 operator *(Vector3 value2);
				Vector3 operator *(float scaleFactor);
				Vector3 operator /(Vector3 value);
				Vector3 operator /=(Vector3 value);
				Vector3 operator /(float divider);
			};

			#pragma region Operators

			//bool operator ==(const Vector3& value1, const Vector3& value2);
			////bool operator ==(Vector3 value1, Vector3 value2);
			////bool operator !=(Vector3 value1, Vector3 value2);
			////Vector3 operator +(Vector3 value1, Vector3 value2);
			//Vector3 operator -(Vector3 value);
			//////Vector3 operator -(Vector3 value1, Vector3 value2);
			////Vector3 operator *(Vector3 value1, Vector3 value2);
			////Vector3 operator *(Vector3 value, float scaleFactor);
			//Vector3 operator *(float scaleFactor, Vector3 value);
			////Vector3 operator /(Vector3 value1, Vector3 value2);
			////Vector3 operator /(Vector3 value, float divider);
			inline Vector3 Vector3::operator *(Vector3 value2)
			{
				Vector3 output;
				output.X = this->X * value2.X;
				output.Y = this->Y * value2.Y;
				output.Z = this->Z * value2.Z;
				return output;
			}

			inline Vector3 Vector3::operator *(float scaleFactor)
			{
				Vector3 output;
				output.X = this->X * scaleFactor;
				output.Y = this->Y * scaleFactor;
				output.Z = this->Z * scaleFactor;
				return output;
			}

			inline Vector3 Vector3::operator /(Vector3 value2)
			{
				Vector3 output;
				output.X = this->X / value2.X;
				output.Y = this->Y / value2.Y;
				output.Z = this->Z / value2.Z;
				return output;
			}

			inline Vector3 Vector3::operator /(float divider)
			{
				Vector3 output;
				output.X = this->X / divider;
				output.Y = this->Y / divider;
				output.Z = this->Z / divider;
				return output;
			}

			inline bool operator ==(const Vector3& value1, const Vector3& value2)
			{
				return value1.X == value2.X
					&& value1.Y == value2.Y
					&& value1.Z == value2.Z;
			}
			inline Vector3 operator *(float scaleFactor, Vector3 value)
			{
				value.X *= scaleFactor;
				value.Y *= scaleFactor;
				value.Z *= scaleFactor;
				return value;
			}

			#pragma endregion
		}
	}
}

#endif